"use strict";

import { Command } from "@p-test-cli/command";
import { log, tools, ejs } from "@p-test-cli/utils";
import inquirer from "inquirer";
import getProjectTemplate from "./getProjectTemplate";
import { Package } from "@p-test-cli/package";
import { homedir } from "os";
import path from "path";

const fse = require("fs-extra");
const kebabCase = require("kebab-case");

const init = (...args) => {
  return new InitCommand(args);
};
const semver = require("semver");
const TYPE_PROJECT = "project";
const TYPE_COMPONENT = "component";
const TEMPLATE_TYPE_NORMAL = "normal";
const TEMPLATE_TYPE_CUSTOM = "custom";
const fs = require("fs");
const userHome = homedir();
const WHITE_COMMAND = ["npm", "cnpm", "pnpm"];
class InitCommand extends Command {
  projectName;
  force;
  template;
  projectInfo;
  templateInfo;
  templateNpm;
  init() {
    this.projectName = this._argv[0] || "";
    this.force = !!this._cmd.force;
    log.verbose("projectName", this.projectName);
    log.verbose("force", this.force);
  }
  async exec() {
    try {
      //准备阶段
      //下载模板
      //安装模板
      const projectInfo = await this.prepare();
      if (projectInfo) {
        this.projectInfo = projectInfo;
        await this.downloadTemplate();
        await this.installTemplate();
      }
    } catch (e) {
      log.error("error", e.message);
    }
  }
  async installTemplate() {
    console.log("9999999999999999999999");
    console.log(this.templateInfo);
    if (this.templateInfo) {
      if (!this.templateInfo.type) {
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
      }
      if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
        //标准安装
        await this.installNormalTemplate();
      } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
        //自定义安装
        await this.installCustomTemplate();
      } else {
        throw new Error("无法识别项目模板类型！");
      }
    } else {
      throw new Error("项目模板信息不存在！");
    }
  }
  async installNormalTemplate() {
    let spinner = tools.spinnerStart("正在安装模板...");

    await tools.sleep();
    const targetPath = process.cwd();
    const templatePath = path.resolve(
      this.templateNpm.cacheFilePath,
      "template"
    );
    console.log(targetPath, templatePath);
    try {
      fse.ensureDirSync(templatePath);
      fse.ensureDirSync(targetPath);
      fse.copySync(templatePath, targetPath);
    } catch (e) {
      throw e;
    } finally {
      spinner.stop();
      log.success("sucess", "模板安装成功");
    }

    // ejs 模板渲染
    const ejsIgnoreFiles = [
      "**/node_modules/**",
      "**/.git/**",
      "**/.vscode/**",
      "**/.DS_Store",
      "**/public/**",
    ];
    if (this.templateInfo.ignore) {
      ejsIgnoreFiles.push(...this.templateInfo.ignore);
    }

    await ejs(targetPath, this.projectInfo, {
      ignore: ejsIgnoreFiles,
    });

    //依赖安装
    const { installCommand, startCommand } = this.templateInfo;

    await this.execCommand(installCommand, "依赖安装过程失败！");
    await this.execCommand(startCommand, "项目启动失败！");
  }
  checkCommand(cmd) {
    if (WHITE_COMMAND.includes(cmd)) {
      return cmd;
    }
    return null;
  }
  async execCommand(command, message) {
    let commandRet;
    if (command) {
      const execCmd = command.split(" ");
      if (execCmd.length > 0) {
        const cmd = this.checkCommand(execCmd[0]);
        if (!cmd) {
          throw new Error("命令不存在！");
        }
        const args = execCmd.slice(1);
        console.log(cmd, args);
        commandRet = await tools.execAsync(cmd, args, {
          stdio: "inherit",
          cwd: process.cwd(),
        });
      }
      if (commandRet !== 0) {
        throw new Error(message);
      }
    }
    return commandRet;
  }

  async installCustomTemplate() {
    console.log("begin888888888888888");
    //查询自定义模板的入口文件
    if (await this.templateNpm.exists()) {
      const rootFile = this.templateNpm.getRootFilePath();
      if (fs.existsSync(rootFile)) {
        log.info("start", "开始执行自定义模板");
        const templatePath = path.resolve(
          this.templateNpm.cacheFilePath,
          "template"
        );
        const options = {
          templateInfo: this.templateInfo,
          projectInfo: this.projectInfo,
          sourcePath: templatePath,
          targetPath: process.cwd(),
        };
        const code = `require('${rootFile}')(${JSON.stringify(options)})`;
        log.verbose("code", code);
        await tools.execAsync("node", ["-e", code], {
          stdio: "inherit",
          cwd: process.cwd(),
        });
      }
    }
  }
  async downloadTemplate() {
    //1.  通过项目模板API获取项目模板信息
    //1.1 通过egg.js搭建一套后端系统
    //1.2 通过npm存储项目模板
    //1.3 将项目模板信息存储到mongodb数据库中
    //1.4 通过egg.js获取mongodb中的数据并通过API返回
    const { projectTemplate } = this.projectInfo;
    const templateInfo = this.template.find(
      (item) => item.npmName === projectTemplate
    );
    const targetPath = path.resolve(userHome, ".p-test-cli", "template");
    const storeDir = path.resolve(
      userHome,
      ".p-test-cli",
      "template",
      "node_modules"
    );
    const { npmName, version } = templateInfo;
    const templateNpm = new Package({
      targetPath,
      storeDir,
      packageName: npmName,
      packageVersion: version,
    });

    if (!(await templateNpm.exists())) {
      const spinner = tools.spinnerStart("正在下载模板...");
      await tools.sleep();
      try {
        await templateNpm.install();
      } catch (e) {
        throw e;
      } finally {
        spinner.stop(true);
        if (await templateNpm.exists()) {
          log.success("sucess", "下载模板成功");
          this.templateInfo = templateInfo;
          this.templateNpm = templateNpm;
        }
      }
    } else {
      const spinner = tools.spinnerStart("正在更新模板...");
      await tools.sleep();

      try {
        await templateNpm.update();
      } catch (e) {
        throw e;
      } finally {
        spinner.stop(true);
        if (await templateNpm.exists()) {
          log.success("sucess", "更新模板成功");
          this.templateInfo = templateInfo;
          this.templateNpm = templateNpm;
        }
      }
    }
  }

  async prepare() {
    //判断目录是否为空
    const template = await getProjectTemplate();
    if (!template || template.length === 0) {
      throw new Error("项目模板不存在！");
    }
    this.template = template;
    //是否强制更新
    //选择创建的项目或者组件
    //获取项目信息
    const localPath = process.cwd();
    if (!this.isDirEmpty(localPath)) {
      //询问是否继续创建
      let ifContinuei = false;
      if (!this.force) {
        const questions: any = [
          {
            type: "confirm",
            name: "ifContinue",
            message: "当前文件夹不为空，是否继续创建项目？",
          },
        ];

        const { ifContinue } = await inquirer
          .prompt(questions)
          .then((answers) => {
            return answers;
          });
        if (!ifContinue) return;
        ifContinuei = ifContinue;
      }
      if (ifContinuei || this.force) {
        const { confirmDelete } = await inquirer
          .prompt({
            type: "confirm",
            name: "confirmDelete",
            default: false,
            message: "是否确认清空当前目录下的文件？",
          } as any)
          .then((answers) => {
            return answers;
          });
        if (confirmDelete) {
          //清空当前目录
          fse.emptyDirSync(localPath);
        }
      }
    }
    return await this.getProjectInfo();
  }
  validName(v) {
    return /^[a-zA-Z]+([-][a-zA-Z0-9]*|[_][a-zA-Z0-9]*|[a-zA-Z0-9])*/.test(v);
  }
  async getProjectInfo() {
    let _self = this;
    let projectInfo = {};
    const questions: any = {
      type: "list",
      name: "type",
      message: "请选择初始化类型",
      default: TYPE_PROJECT,
      choices: [
        {
          name: "项目",
          value: TYPE_PROJECT,
        },
        {
          name: "组件",
          value: TYPE_COMPONENT,
        },
      ],
    };
    let isProjectNameValid = false;
    if (_self.validName(this.projectName)) {
      isProjectNameValid = true;
      projectInfo["projectName"] = this.projectName;
    }
    const { type } = await inquirer.prompt(questions).then((e) => e);
    this.template = this.template.filter((v) => v.tag.includes(type));

    const title = type === TYPE_PROJECT ? "项目" : "组件";
    let projectInfoQuestions = [];
    if (!isProjectNameValid) {
      projectInfoQuestions.push({
        type: "input",
        name: "projectName",
        message: `请输入${title}名称`,
        default: "",
        validate: function (v) {
          //1.首字符为英文字符
          //2.尾字符必须为英文或数字，不能为字符
          //3.字符仅允许"-_"
          if (_self.validName(v)) return true;
          return `请输入合法的${title}名称`;
        },
        filter: (v) => typeof v === "string",
      });
    }
    const basicQ = [
      {
        type: "input",
        name: "projectVersion",
        message: `请输入${title}版本号`,
        default: "1.0.0",
        validate(v) {
          if (!!semver.valid(v)) return true;
          return `请输入合法的${title}版本号`;
        },
        filter: (v) => {
          return !!semver.valid(v) ? semver.valid(v) : v;
        },
      },
      {
        type: "list",
        name: "projectTemplate",
        message: `请选择${title}模板`,
        choices: this.createTemplateChoices(),
      },
    ];
    projectInfoQuestions = [...projectInfoQuestions, ...basicQ];

    if (type === TYPE_PROJECT) {
      const project = await inquirer
        .prompt(projectInfoQuestions)
        .then((e) => e);
      projectInfo = {
        ...projectInfo,
        type,
        ...project,
      };
    } else if (type === TYPE_COMPONENT) {
      const desQue = {
        type: "input",
        name: "projectDescription",
        message: `请输入${title}描述`,
        default: "",
        validate: function (v) {
          if (v) return true;
          return `请输入描述信息`;
        },
      };
      projectInfoQuestions.push(desQue);
      const project = await inquirer
        .prompt(projectInfoQuestions)
        .then((e) => e);
      projectInfo = {
        ...projectInfo,
        type,
        ...project,
      };
    }
    //生成className
    if (projectInfo["projectName"]) {
      projectInfo["className"] = kebabCase(projectInfo["projectName"]).replace(
        /^-/,
        ""
      );
    }
    if (projectInfo["projectVersion"]) {
      projectInfo["version"] = projectInfo["projectVersion"];
    }
    if (projectInfo["projectDescription"]) {
      projectInfo["description"] = projectInfo["projectDescription"];
    }
    return projectInfo;
  }
  createTemplateChoices() {
    return this.template.map((item) => ({
      value: item.npmName,
      name: item.name,
    }));
  }
  isDirEmpty(localPath) {
    let fileList = fs.readdirSync(localPath);
    fileList = fileList.filter(
      (file) => !file.startsWith(".") && ["node_modules"].indexOf(file) < 0
    );
    return !fileList || fileList.length <= 0;
  }
}

export = init;
